package com.group.project.hrms.utils;

import com.alibaba.fastjson.JSONArray;
import com.group.project.hrms.entity.*;
import com.group.project.hrms.entity.UsefulEntity.DayLeaveInfo;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 该类是针对考勤计算的专用工具类
 */
public class AttendanceUtil {


    //用于缓存统计计算结果，以便归档时直接写入数据库
    public static List<AttendanceMonthlyReport> reportList;

    @Autowired
    private DateUtil dateUtil;


    /**
     * 传入通讯录，返回以员工工号为Key，通讯录对象为Value的Map
     * @param list
     * @return
     */
    public Map<String, ContactBasic> getContactBasicsMap(List<ContactBasic> list){
        Map<String, ContactBasic> resultMap = new HashMap<>();
        if(null != list && list.size() != 0){
            for (ContactBasic cb : list
                 ) {
                resultMap.put(cb.getStaffId(), cb);
            }
        }
        return resultMap;
    }


    /**
     * 传入原始打卡记录，返回以钉钉userid为key，打卡记录集合为Value的Map
     * @param records
     * @return
     */
    public Map<String, List<DingClockRecord>> getDingClockRecordMap(List<DingClockRecord> records){
        return records.stream().collect(Collectors.groupingBy(DingClockRecord::getUserid));
    }

    /**
     * 传入原始打卡记录，返回以打卡时间的日期为key，打卡记录集合为Value的Map
     * @param records
     * @return
     */
    public Map<Integer, List<DingClockRecord>> getDingClockRecordMapGrByDay(List<DingClockRecord> records){
        return records.stream().collect(Collectors.groupingBy(item -> item.getUserchecktime().getDate()));
    }


    /**
     * 传入原始月度请休假记录，返回以钉钉userid为key，休假记录对象集合为Value的 Map
     * @param records
     * @return
     */
    public Map<String, List<DingLeaveRecord>> getDingLeaveRecordMap(List<DingLeaveRecord> records){
        return records.stream().collect(Collectors.groupingBy(DingLeaveRecord::getUserid));
    }

    /**
     * 下列两个方法是将钉钉中的休假记录分别按照起始日期和结束日期进行分组排序
     * @param records
     * @return
     */
    public Map<Integer, List<DingLeaveRecord>> getDingLeaveRecordMapGrByStartTime(List<DingLeaveRecord> records){
        return records.stream().collect(Collectors.groupingBy(item -> item.getStartTime().getDate()));
    }
    public Map<Integer, List<DingLeaveRecord>> getDingLeaveRecordMapGrByEndTime(List<DingLeaveRecord> records){
        return records.stream().collect(Collectors.groupingBy(item -> item.getEndTime().getDate()));
    }


    /**
     * 传入一线生产考勤请休假记录，返回以ContactID为Key，记录对象为Value的 Map
     * @param records
     * @return
     */
    public Map<String, List<FrontLineVacationRecord>> getFrontLineVacationRecordMap(List<FrontLineVacationRecord> records){
        return records.stream().collect(Collectors.groupingBy(FrontLineVacationRecord::getContactId));
    }

    /**
     * 下列两个方法是将一线生产人员的休假记录分别按照起始日期和结束日期进行分组排序
     * @param records
     * @return
     */
    public Map<Integer, List<FrontLineVacationRecord>> getFrontLineVacationRecordMapGrByStartTime(List<FrontLineVacationRecord> records){
        return records.stream().collect(Collectors.groupingBy(item -> item.getStartTime().getDate()));
    }
    public Map<Integer, List<FrontLineVacationRecord>> getFrontLineVacationRecordMapGrByEndTime(List<FrontLineVacationRecord> records){
        return records.stream().collect(Collectors.groupingBy(item -> item.getEndTime().getDate()));
    }


    /**
     * 传入一线生产考勤额外加班每日的动态调整记录，返回以ContactID为key，记录对象为Value的 Map
     * @param records
     * @return
     */
    public Map<String, List<FrontLineDayAdjustRecord>> getFrontLineDayAdjustRecordMap(List<FrontLineDayAdjustRecord> records){
        return records.stream().collect(Collectors.groupingBy(FrontLineDayAdjustRecord::getContactId));
    }

    /**
     * 将一线生产人员的日动态调整的额外加班记录分别按照日期进行分组排序
     * @param records
     * @return
     */
    public Map<Integer, List<FrontLineDayAdjustRecord>> getFrontLineDayAdjustRecordMapGrByDay(List<FrontLineDayAdjustRecord> records){
        return records.stream().collect(Collectors.groupingBy(item -> item.getFirstCheckTimeInDay().getDate()));
    }



    /**
     * 传入考勤组相关List，形成以ID为Key，对象为Value的Map
     * @param list
     * @return
     */
    public Map<String, AttendanceGroupInfo> getAttendanceGroupInfoMap(List<AttendanceGroupInfo> list){
        Map<String, AttendanceGroupInfo> resultMap = new HashMap<>();
        if(null != list && list.size() != 0){
            for (AttendanceGroupInfo info : list
            ) {
                resultMap.put(info.getAttendanceGroupId(), info);
            }
        }
        return resultMap;
    }
    public Map<String, CheckPointInfo> getCheckPointInfoMap(List<CheckPointInfo> records){
        Map<String, CheckPointInfo> resultMap = new HashMap<>();
        if(null != records){
            for (CheckPointInfo cp : records
                 ) {
                resultMap.put(cp.getCheckPointId(), cp);
            }
        }
        return resultMap;
    }
    public Map<String, OvertimeRules> getOvertimeRulesMap(List<OvertimeRules> records){
        Map<String, OvertimeRules> resultMap = new HashMap<>();
        if(null != records){
            for (OvertimeRules rule : records
                 ) {
                resultMap.put(rule.getRulesId(), rule);
            }
        }
        return resultMap;
    }
    public Map<String, AttendanceShiftInfo> getAttendanceShiftInfoMap(List<AttendanceShiftInfo> records){
        Map<String, AttendanceShiftInfo> resultMap = new HashMap<>();
        if(null != records){
            for (AttendanceShiftInfo shiftInfo : records
                 ) {
                resultMap.put(shiftInfo.getShiftId(), shiftInfo);
            }
        }
        return resultMap;
    }
    public Map<String, RestTimeSettings> getRestTimeSettingsMap(List<RestTimeSettings> records){
        Map<String, RestTimeSettings> resultMap = new HashMap<>();
        if(null != records){
            for (RestTimeSettings rs : records
                 ) {
                resultMap.put(rs.getRestTimeId(), rs);
            }
        }
        return resultMap;
    }
    public Map<String, SpecialCheckDate> getSpecialCheckDateMap(List<SpecialCheckDate> records){
        Map<String, SpecialCheckDate> resultMap = new HashMap<>();
        if(null != records && records.size() != 0){
            for (SpecialCheckDate sp : records
                 ) {
                resultMap.put(sp.getSpecialCheckId(), sp);
            }
        }
        return resultMap;
    }


    /**
     * 传入考勤组和部门的对应关系集合，返回以部门ID为Key, 考勤组ID为Value的 Map
     * @param records
     * @return
     */
    public Map<String, String> getGroupDeptMap(List<AttendanceGroupDepts> records){
        Map<String, String> resultMap = new HashMap<>();
        if(null != records){
            for (AttendanceGroupDepts ag : records
            ) {
                resultMap.put(ag.getDeptId().toString(), ag.getAttendanceGroupId());
            }
        }
        return resultMap;
    }


    /**
     * 传入考勤组和人员的对应关系集合，返回以人员ID为Key, 考勤组ID为Value的 Map
     * @param records
     * @return
     */
    public Map<String, String> getGroupStaffMap(List<AttendanceGroupStaffs> records){
        Map<String, String> resultMap = new HashMap<>();
        if(null != records){
            for (AttendanceGroupStaffs ag : records
            ) {
                resultMap.put(ag.getContactId(), ag.getAttendanceGroupId());
            }
        }
        return resultMap;
    }


    /**
     * 传入考勤组动态调整记录，含部门和人员两张表，返回部门ID和人员ID为Key, 对象集合为值的 Map
     * @param records
     * @return
     */
    public Map<Long, List<AttendanceGroupDeptAdjustRecord>> getAttendanceGroupDeptAdjustRecordMap(List<AttendanceGroupDeptAdjustRecord> records){
        return records.stream().collect(Collectors.groupingBy(AttendanceGroupDeptAdjustRecord::getDeptId));
    }
    public Map<String, List<AttendanceGroupStaffAdjustRecord>> getAttendanceGroupStaffAdjustRecordMap(List<AttendanceGroupStaffAdjustRecord> records){
        return records.stream().collect(Collectors.groupingBy(AttendanceGroupStaffAdjustRecord::getContactId));
    }

    /**
     * 传入两个钉钉休假记录，去除重复记录后的合并集合
     * @param list1
     * @param list2
     * @return
     */
    public List<DingLeaveRecord> getDayFinalLeaveRecords(List<DingLeaveRecord> list1, List<DingLeaveRecord> list2){
        List<DingLeaveRecord> finalList = new ArrayList<>();
        if(null != list1 && list1.size() != 0 && null != list2 && list2.size() != 0){
            finalList.addAll(list1);
            for (DingLeaveRecord dr1 : list1
            ) {
                for (DingLeaveRecord dr2 : list2
                ) {
                    if(dr2.getId().longValue() != dr1.getId().longValue()){
                        finalList.add(dr2);
                    }
                }
            }
            return finalList;
        }else if(null == list2 || list2.size() == 0){
            return list1;
        }else {
            return list2;
        }
    }

    /**
     * 去掉传入集合的重复数据（比较ID）
     * @param list
     * @return
     */
    public List<DingLeaveRecord> getFinalLeaveRecords(List<DingLeaveRecord> list){
        List<DingLeaveRecord> finalList = new ArrayList<>();
        if(null != list && list.size() != 0){
            for (DingLeaveRecord dr : list
                 ) {
                if(finalList.size() == 0){
                    finalList.add(dr);
                }else {
                    for (DingLeaveRecord finalDr : finalList
                         ) {
                        if(finalDr.getId() != dr.getId()){
                            finalList.add(dr);
                        }
                    }
                }
            }
        }
        return finalList;
    }


    /**
     * 传入两个一线生产人员的休假记录，去除重复记录后的合并集合
     * @param list1
     * @param list2
     * @return
     */
    public List<FrontLineVacationRecord> getDayFinalFrontLineVacationRecords(List<FrontLineVacationRecord> list1, List<FrontLineVacationRecord> list2){
        List<FrontLineVacationRecord> finalList = new ArrayList<>();
        if(null != list1 && list1.size() != 0 && null != list2 && list2.size() != 0){
            finalList.addAll(list1);
            for (FrontLineVacationRecord dr1 : list1
            ) {
                for (FrontLineVacationRecord dr2 : list2
                ) {
                    if(dr2.getId().longValue() != dr1.getId().longValue()){
                        finalList.add(dr2);
                    }
                }
            }
            return finalList;
        }else if(null == list2 || list2.size() == 0){
            return list1;
        }else{
            return list2;
        }
    }

    /**
     * 去除传入集合的重复对象
     * @param list
     * @return
     */
    public List<FrontLineVacationRecord> getFinalFrontLineVacationRecords(List<FrontLineVacationRecord> list){
        List<FrontLineVacationRecord> finalList = new ArrayList<>();
        if(null != list && list.size() != 0){
            for (FrontLineVacationRecord fvr : list
                 ) {
                if(finalList.size() == 0){
                    finalList.add(fvr);
                }else {
                    for (FrontLineVacationRecord finalFvr : finalList
                         ) {
                        if(finalFvr.getId() != fvr.getId()){
                            finalList.add(fvr);
                        }
                    }
                }
            }
        }
        return finalList;
    }


    /**
     * 传入钉钉休假类型，返回以休假类型编码为Key,休假类型对象为Value的Map
     * @param vacationTypeList
     * @return
     */
    public Map<String, DingVacationType> getVacationTypeMap(List<DingVacationType> vacationTypeList){
        Map<String, DingVacationType> vacationTypeMap = new HashMap<>();
        if(null != vacationTypeList && vacationTypeList.size() != 0){
            for (DingVacationType dingVacationType : vacationTypeList
            ) {
                vacationTypeMap.put(dingVacationType.getLeaveCode(), dingVacationType);
            }
        }
        return vacationTypeMap;
    }


    /**
     * 此函数主要应用于计算一个员工在某一天的休假，不包含跨2天以上的休假
     * @param leaveCode 休假类型
     * @param startTime 休假开始时间
     * @param endTime 休假结束时间
     * @param onDutyTime 当天的上班时间
     * @param offDutyTime 当天的下班时间
     * @param restTimeIDs 考勤设置中的休息时段
     * @param nowDay 当天日期，例如：2022-05-01
     * @param restTimeSettingMap 休息时段数据库Map集合
     * @param vacationTypeMap 考勤类型数据库Map集合
     * @param casualLeaveHours 当日事假总计
     * @param medicalLeaveHours 当日病假总计
     * @param annualLeaveHours 当日年假总计
     * @param maternityLeaveHours 当日产假总计
     * @param marriageLeaveHours 当日婚假总计
     * @param bereavementLeaveHours 当日丧假总计
     * @param othersLeaveHours 当日其它假总计
     * @return 计算后的各种假的集合
     */
    public Map<String, DayLeaveInfo> getUserLeaveHours(String leaveCode, Date startTime, Date endTime, Date onDutyTime, Date offDutyTime, JSONArray restTimeIDs,
                                                 String nowDay, Map<String, RestTimeSettings> restTimeSettingMap, Map<String, DingVacationType> vacationTypeMap,
                                                 float casualLeaveHours, float medicalLeaveHours, float annualLeaveHours, float maternityLeaveHours,
                                                 float marriageLeaveHours, float bereavementLeaveHours, float othersLeaveHours, float exchangeLeaveHours, float maxDayWorkingHours){
        Map<String, DayLeaveInfo> leaveMap = new HashMap<>();
        Date startDate = null;
        Date endDate = null;
        //创建日休假记录计算后的结果信息对象
        DayLeaveInfo leaveInfo = new DayLeaveInfo();
        if(startTime.getTime() < onDutyTime.getTime()){
            //请假开始时间比上班时间早，则计算开始时间以上班时间为准
            startDate = onDutyTime;
        }else if(startTime.getTime() <= offDutyTime.getTime()){
            //请假时间在上班时间和下班时间之间，则计算开始时间以请假开始时间为准
            startDate = startTime;
        }
        if(endTime.getTime() >= onDutyTime.getTime() && endTime.getTime() <= offDutyTime.getTime()){
            //请假结束时间在上班时间和下班时间之间，则计算结束时间以请假结束时间为准
            endDate = endTime;
        }else if(endTime.getTime() > offDutyTime.getTime()){
            //如果请假结束时间在下班时间之后，则计算结束时间以下班时间为准
            endDate = offDutyTime;
        }
        if(null != startDate && null != endDate){
            //如果计算开始时间和计算结束时间都不为null，则说明这是一个有效的休假，同时需要扣除休息时段
            long dayLeaveMillTime = endDate.getTime() - startDate.getTime();
            if(null != restTimeIDs){
                for (Object rid : restTimeIDs
                ) {
                    RestTimeSettings settings = restTimeSettingMap.get(rid);
                    Date restStart = dateUtil.getDateByPointDate(nowDay, settings.getStartTime());
                    Date restEnd = dateUtil.getDateByPointDate(nowDay, settings.getEndTime());
                    //判断当天的休假开始和结束时间是否包含每个休息时段，包含就扣除
                    if(startDate.getTime() <= restStart.getTime() && endDate.getTime() >= restEnd.getTime()){
                        dayLeaveMillTime = dayLeaveMillTime - (restEnd.getTime() - restStart.getTime());
                    }
                }
            }
            //当非跨天的请假，计算后的总小时数超过了员工所在班次的最大工时时，直接将最大工时值赋予当天请假
            float dayLeaveTime = dateUtil.getHoursByMills(dayLeaveMillTime) > maxDayWorkingHours ? maxDayWorkingHours : dateUtil.getHoursByMills(dayLeaveMillTime);
            leaveInfo.setDayLeaveTimeStart(startDate);
            leaveInfo.setDayLeaveTimeEnd(endDate);
            //根据休假类型，区分并计算每个休假的时间
            DingVacationType dingVacationType = vacationTypeMap.get(leaveCode);
            if(null != dingVacationType){
                if(dingVacationType.getLeaveName().equals("事假")){
                    casualLeaveHours = casualLeaveHours + dayLeaveTime;
                    leaveInfo.setCount(casualLeaveHours);
                    leaveMap.put("casualLeaveHours", leaveInfo);
                }
                else if(dingVacationType.getLeaveName().equals("病假")){
                    medicalLeaveHours = medicalLeaveHours + dayLeaveTime;
                    leaveInfo.setCount(medicalLeaveHours);
                    leaveMap.put("medicalLeaveHours", leaveInfo);
                }
                else if(dingVacationType.getLeaveName().equals("年假")){
                    annualLeaveHours = annualLeaveHours + dayLeaveTime;
                    leaveInfo.setCount(annualLeaveHours);
                    leaveMap.put("annualLeaveHours", leaveInfo);
                }
                else if(dingVacationType.getLeaveName().equals("产假")){
                    maternityLeaveHours = maternityLeaveHours + dayLeaveTime;
                    leaveInfo.setCount(maternityLeaveHours);
                    leaveMap.put("maternityLeaveHours", leaveInfo);
                }
                else if(dingVacationType.getLeaveName().equals("婚假")){
                    marriageLeaveHours = marriageLeaveHours + dayLeaveTime;
                    leaveInfo.setCount(marriageLeaveHours);
                    leaveMap.put("marriageLeaveHours", leaveInfo);
                }
                else if(dingVacationType.getLeaveName().equals("丧假")){
                    bereavementLeaveHours = bereavementLeaveHours + dayLeaveTime;
                    leaveInfo.setCount(bereavementLeaveHours);
                    leaveMap.put("bereavementLeaveHours", leaveInfo);
                }
                else if(dingVacationType.getLeaveName().equals("调休")){
                    exchangeLeaveHours = exchangeLeaveHours + dayLeaveTime;
                    leaveInfo.setCount(exchangeLeaveHours);
                    leaveMap.put("exchangeLeaveHours", leaveInfo);
                }else {
                    othersLeaveHours = othersLeaveHours + dayLeaveTime;
                    leaveInfo.setCount(othersLeaveHours);
                    leaveMap.put("othersLeaveHours", leaveInfo);
                }
            }
        }
        return leaveMap;
    }

    /**
     * 在实际出勤的计算中，比较打卡时间与当天请休假之前的包含关系，当打卡时间大于等于休假开始时间或者打卡时间小于等于休假结束时间时，返回True
     * @param mapList
     * @param checkTime
     * @return
     */
    public boolean checkTimeInLeaveRecords(List<Map<String, DayLeaveInfo>> mapList, Date checkTime){
        if(mapList.size() != 0){
            for (Map<String, DayLeaveInfo> map : mapList
                 ) {
                DayLeaveInfo dayLeaveInfo = null;
                if(null != map.get("casualLeaveHours")){
                    dayLeaveInfo = map.get("casualLeaveHours");
                }else if(null != map.get("medicalLeaveHours")){
                    dayLeaveInfo = map.get("medicalLeaveHours");
                }else if(null != map.get("annualLeaveHours")){
                    dayLeaveInfo = map.get("annualLeaveHours");
                }else if(null != map.get("maternityLeaveHours")){
                    dayLeaveInfo = map.get("maternityLeaveHours");
                }else if(null != map.get("marriageLeaveHours")){
                    dayLeaveInfo = map.get("marriageLeaveHours");
                }else if(null != map.get("bereavementLeaveHours")){
                    dayLeaveInfo = map.get("bereavementLeaveHours");
                }else if(null != map.get("othersLeaveHours")){
                    dayLeaveInfo = map.get("othersLeaveHours");
                }else if(null != map.get("exchangeLeaveHours")){
                    dayLeaveInfo = map.get("exchangeLeaveHours");
                }
                if(null != dayLeaveInfo){
                    if(checkTime.getTime() >= dayLeaveInfo.getDayLeaveTimeStart().getTime() || checkTime.getTime() <= dayLeaveInfo.getDayLeaveTimeEnd().getTime()){
                        return true;
                    }
                }
            }
        }
        return false;
    }

    /**
     * 传入通讯录档案信息，形成以通信录ID为Key，对象为Value的Map
     * @param list
     * @return
     */
    public Map<String, ContactArchivesWorking> getContactWorkingMap(List<ContactArchivesWorking> list){
        Map<String, ContactArchivesWorking> map = new HashMap<>();
        if(null != list && list.size() != 0){
            for (ContactArchivesWorking caw : list
                 ) {
                map.put(caw.getContactId(), caw);
            }
        }
        return map;
    }


    /**
     * 筛选跨天的请假日期和请假类型
     * @param dingLeaveRecords
     * @param frontVacationRecords
     * @param firstDay
     * @param lastDay
     * @return
     */
    public Map<Integer, String> getLeaveRangeDate(List<DingLeaveRecord> dingLeaveRecords, List<FrontLineVacationRecord> frontVacationRecords, Date firstDay, Date lastDay){
        Map<Integer, String> map = new HashMap<>();
        int month = dateUtil.getMonthByDate(firstDay);
        List list = new ArrayList();
        for (int i = 0; i < 2; i++) {
            if(i == 0){
                list = dingLeaveRecords;
            }
            if(i == 1){
                list = frontVacationRecords;
            }
            if(null != list && list.size() != 0){
                for (Object obj : list
                ) {
                    Date start = null;
                    Date end = null;
                    String type = null;
                    if(i == 0){
                        DingLeaveRecord d = (DingLeaveRecord) obj;
                        start = d.getStartTime();
                        end = d.getEndTime();
                        type = d.getLeaveCode();
                    }
                    if(i == 1){
                        FrontLineVacationRecord f = (FrontLineVacationRecord) obj;
                        start = f.getStartTime();
                        end = f.getEndTime();
                        type = f.getVacationTypeCode();
                    }
                    if(month != dateUtil.getMonthByDate(start)){ //开始时间一定在上个月
                        if(dateUtil.getDayByDate(end) - 1 > 1){
                            for (int j = 1; j < dateUtil.getDayByDate(end); j++) {
                                map.put(j, type);
                            }
                        }
                    }else if(month != dateUtil.getMonthByDate(end)){ //结束时间一定在下个月
                        if(dateUtil.getDayByDate(lastDay) - dateUtil.getDayByDate(start) > 1){
                            for (int j = dateUtil.getDayByDate(start) + 1; j <= dateUtil.getDayByDate(lastDay); j++) {
                                map.put(j, type);
                            }
                        }
                    }else { //开始时间和结束时间都在当月
                        if(dateUtil.getDayByDate(end) - dateUtil.getDayByDate(start) > 1){
                            for (int j = dateUtil.getDayByDate(start) + 1; j < dateUtil.getDayByDate(end); j++) {
                                map.put(j, type);
                            }
                        }
                    }
                }
            }
        }
        return map;
    }

    /**
     * 传入节假日集合，返回以节假日天（号）为Key，节假日对象为Value的Map
     * @param list
     * @return
     */
    public Map<Integer, HolidayRecord> getHolidayRecordMap(List<HolidayRecord> list){
        Map<Integer, HolidayRecord> resultMap = new HashMap<>();
        for (HolidayRecord hr : list
             ) {
            int day = dateUtil.getDayByDate(hr.getAppointDate());
            resultMap.put(day, hr);
        }
        return resultMap;
    }


    /**
     * 传入一个Float （例如 1.6） 对这个数进行向上取整  结果为 1.5
     * @param number
     * @return
     */
    public float doRoundingUp(float number){
        if(number % 1 < 0.5){
            number = (int) number;
        }else if(number % 1 == 0.5){
            //不用处理
        }else {
            number = (int) number + 0.5F;
        }
        return number;
    }

    /**
     * 传入一个Float （例如 1.6） 对这个数进行向上取整  结果为 2.0
     * @param number
     * @return
     */
    public float doRoundingDown(float number){
        if(number % 1 < 0.5){
            number = (int) number + 0.5F;
        }else if(number % 1 == 0.5){
            //不用处理
        }else {
            number = (int) number + 1.0F;
        }
        return number;
    }


    /**
     * 待完成
     * @return
     */
    public AttendanceMonthlyReport doComputDayAttendanceReport(){
        AttendanceMonthlyReport dailyReport = new AttendanceMonthlyReport(0F);

        return dailyReport;
    }


}
